package com.umeox.iqibla.qimi_app.protocol

import android.util.Log
import com.umeox.iqibla.qimi_app.MyHttpLoggingInterceptor
import com.umeox.iqibla.qimi_app.WSCallback
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString
import org.json.JSONObject
import java.util.concurrent.TimeUnit

class WebsocketProtocol(
    private val url: String,
    private val accessToken: String,
    private val appKey: String,
    private val userId: String,
    private val agentId: String,
) : Protocol() {

    companion object {
        private const val TAG = "WebsocketProtocol"
    }

    private var websocket: WebSocket? = null

    private val client = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .addInterceptor(MyHttpLoggingInterceptor {
            Log.d(TAG, it)
        })
        .build()

    private var isOpen = false

    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    private val helloReceived = CompletableDeferred<Boolean>()

    private var serverSampleRate: Int = -1

    private var wsCallback: WSCallback? = null

    fun setWSCallback(callback: WSCallback?) {
        wsCallback = callback
    }

    private val webSocketListener = object : WebSocketListener() {
        override fun onOpen(webSocket: WebSocket, response: Response) {
            isOpen = true
            Log.d(TAG, "WebSocket connected")
            scope.launch {
                wsCallback?.onConnected()
            }
            val helloMessage = JSONObject().apply {
                put("type", "hello")
                put("version", 1)
//                put("features", JSONObject().apply {
//                    put("mcp", true)
//                })
                put("transport", "websocket")
                put("audio_params", JSONObject().apply {
                    put("format", "opus")
                    put("sample_rate", 16000)
                    put("channels", 1)
                    put("frame_duration", 60)
                })
            }
            Log.i(TAG, "WebSocket hello: $helloMessage")
            websocket?.send(helloMessage.toString())
        }

        override fun onMessage(webSocket: WebSocket, text: String) {
            Log.i(TAG, "WebSocket message: $text")
            try {
                val json = JSONObject(text)
                val type = json.optString("type")
                when (type) {
                    "hello" -> parseServerHello(json)
                    else -> wsCallback?.onMessageIncoming(json)
                }
            } catch (e:Exception) {
                e.printStackTrace()
            }
        }

        override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
            wsCallback?.onAudioIncoming(bytes.toByteArray())
        }

        override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
            super.onClosing(webSocket, code, reason)
        }

        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
            isOpen = false
            Log.i(TAG, "WebSocket closed: $code: $reason")
            scope.launch {
                wsCallback?.onDisConnected()
            }
            websocket = null
//            reconnect()
        }

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
            isOpen = false
            t.printStackTrace()
            Log.e(TAG, "WebSocket error: ${t.message}")
            scope.launch {
                wsCallback?.onConnectFailed("WebSocket error: ${t.message}")
            }
            websocket = null
//            reconnect()
        }
    }

//    private fun reconnect() {
//        scope.launch {
//            wsCallback?.onConnecting()
//            openAudioChannel()
//        }
//    }

    /**
     *{
     *   "type": "hello",
     *   "transport": "websocket",
     *   "session_id": "xxx",
     *   "audio_params": {
     *     "format": "opus",
     *     "sample_rate": 24000,
     *     "channels": 1,
     *     "frame_duration": 60
     *   }
     * }
     */
    private fun parseServerHello(root: JSONObject) {
        val transport = root.optString("transport")
        if (transport != "websocket") {
            Log.e(TAG, "Unsupported transport: $transport")
            return
        }

        val audioParams = root.optJSONObject("audio_params")
        audioParams?.let {
            val sampleRate = it.optInt("sample_rate", -1)
            if (sampleRate != -1) {
                serverSampleRate = sampleRate
            }
        }
        sessionId = root.optString("session_id")
        helloReceived.complete(true)
    }

    override suspend fun start() {
        // 空实现，与 C++ 一致
    }

    override suspend fun openAudioChannel(): Boolean = withContext(Dispatchers.IO) {
        closeAudioChannel()

        //创建WebSocket 请求
        val request = Request.Builder()
            .url(url)
            .addHeader("MC-App-Key", appKey)
            .addHeader("MC-User-ID", userId)
            .addHeader("MC-Agent-ID", agentId) //
            .addHeader("Authorization", "Bearer $accessToken") //
            .build()
        request.headers.forEach { (name, value) ->
            Log.i(TAG, "Header: $name: $value")
        }
        //初始化WebSocket
        websocket = client.newWebSocket(request, webSocketListener)
        // 防止client在连接建立后立即销毁
        // client.dispatcher.executorService.shutdown()
        try {
            withTimeout(10 * 1000) {
                Log.i(TAG, "Waiting for server hello")
                helloReceived.await()
                Log.i(TAG, "Server hello received")
                true
            }
        } catch (e: Exception) {
            Log.e(TAG, "Failed to receive server hello")
            scope.launch {
                wsCallback?.onConnectFailed("Server timeout")
            }
            closeAudioChannel()
            false
        }
    }

    override fun closeAudioChannel() {
        websocket?.close(1000, "Normal closure")
        websocket = null
    }


    override fun dispose() {
        scope.cancel()
        closeAudioChannel()
        client.dispatcher.executorService.shutdown()
    }

    override suspend fun sendText(text: String) {
        Log.i(TAG, "Sending text: $text")
        websocket?.run {
            send(text)
        } ?: Log.e(TAG, "WebSocket is null")
    }

    override suspend fun sendAudio(data: ByteArray) {
        // Log.i(TAG, "Sending audio: ${data.size}")
        websocket?.run {
            send(ByteString.of(*data))
        } ?: Log.e(TAG, "WebSocket is null")
    }
}